﻿using Apewer.Web;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;

using static Apewer.NumberUtility;
using static Apewer.Web.ApiUtility;

namespace Apewer.AspNetBridge
{

    /// <summary>桥接 ASP.NET 的控制器。</summary>
    public class BridgeController : Web.ApiController
    {

        #region static routes

        static RouteItem[] _routes = null;

        /// <summary>导出所有路由项。</summary>
        public static Json ExportRoutes()
        {
            var array = Json.NewArray();
            if (_routes != null)
            {
                foreach (var route in _routes)
                {
                    var item = route.ToJson();
                    array.AddItem(item);
                }
            }
            return array;
        }

        /// <summary>初始化路由。</summary>
        /// <param name="assemblies">包含控制器的程序集。</param>
        /// <param name="withVoid">包含返回 System.Void 的方法。</param>
        public static void Initialize(IEnumerable<Assembly> assemblies, bool withVoid = false) => _routes = RouteItem.Parse(assemblies, withVoid);

        /// <summary>初始化路由，不包含返回 System.Void 的方法。</summary>
        /// <param name="assemblies">包含控制器的程序集。</param>
        public static void Initialize(params Assembly[] assemblies) => _routes = RouteItem.Parse(assemblies, false);

        #endregion

        #region events

        /// <summary>发生异常时候的处理程序。</summary>
        public static OnException OnException { get; set; }

        /// <summary>输出异常。</summary>
        /// <exception cref="ArgumentNullException"></exception>
        public static void Output(ApiRequest request, ApiResponse response, MethodInfo method, Exception exception)
        {
            if (request == null) throw new ArgumentNullException(nameof(request));
            if (response == null) throw new ArgumentNullException(nameof(response));
            if (method == null) throw new ArgumentNullException(nameof(method));
            if (exception == null) throw new ArgumentNullException(nameof(exception));

            var sb = new StringBuilder();
            if (request.IP.NotEmpty())
            {
                sb.Append(request.IP);
                sb.Append(" ");
            }
            sb.Append(request.Method.ToString());
            if (request.Url != null)
            {
                sb.Append(" ");
                sb.Append(request.Url.OriginalString);
            }
            if (method != null)
            {
                sb.Append("\r\n");
                sb.Append(method.DeclaringType.FullName);
                sb.Append(".");
                sb.Append(method.Name);
            }
            sb.Append("\r\n\r\n");
            sb.Append(new ApiExceptionModel(exception).ToString());

            var model = new ApiTextModel(sb.ToString());
            model.Status = 500;
            response.Model = model;
        }

        /// <summary>使用格式化程序处理所有响应。</summary>
        /// <remarks>
        /// <para>默认值：False（ Bridge ）</para>
        /// <para>True：所有响应均使用自定义格式化程序，忽略 Bridge 内置程序。</para>
        /// <para>False：优先使用 Bridge 内置程序格式化，对不支持的类型使用自定义程序。</para>
        /// </remarks>
        public static bool FormatAll { get; set; }

        /// <summary>自定义格式化程序。</summary>
        public static Func<object, ApiModel> Formatter { get; set; }

        #endregion

        #region instance

        /// <summary></summary>
        public BridgeController() : base((c) => ((BridgeController)c).Execute(), (c) => ((BridgeController)c).Default()) { }

        bool Execute()
        {
            if (_routes == null) Initialize();

            var routes = _routes;
            var route = RouteItem.Match(routes, Request.Url.AbsolutePath, Request.Method);
            try
            {
                Execute(route);
            }
            catch (Exception ex)
            {
                Logger.Write("Route Error", ex.InnerException.GetType().FullName, ex.InnerException.Message);
                var action = OnException;
                if (action != null) action.Invoke(Request, Response, route.Method, ex.InnerException);
                else Response.Model = new ApiStatusModel(500);
            }
            return false;
        }

        void Default() { }

        void Execute(RouteItem route)
        {
            if (route == null)
            {
                Response.Model = new ApiStatusModel(404);
                return;
            }

            // 检查 HTTP 方法
            switch (Request.Method)
            {
                case Network.HttpMethod.GET:
                    if (!route.Get)
                    {
                        Response.Model = new ApiStatusModel(405);
                        return;
                    }
                    break;
                case Network.HttpMethod.POST:
                    if (!route.Post)
                    {
                        Response.Model = new ApiStatusModel(405);
                        return;
                    }
                    break;
                default:
                    Response.Model = new ApiStatusModel(405);
                    return;
            }

            // 准备参数。
            var pis = CollectionUtility.Vacuum(route.Parameters.Map(x => ApiParameter.Parse(x)));
            var ps = ReadParameters(Request, route.Parameters);

            // 准备控制器。
            var c = Activator.CreateInstance(route.Controller) as ApiController;
            c.Request = Request;
            c.Response = Response;

            // 调用 API 方法。
            var r = route.Method.Invoke(c, ps);

            // 格式化、返回。
            if (route.Return == null || route.Return.Equals(typeof(void))) return;
            Response.Model = Format(r) ?? new ApiStatusModel(204);
        }

        static ApiModel Format(object value)
        {
            var isNull = value.IsNull();

            var all = FormatAll;
            var formatter = Formatter;
            var customized = formatter != null;

            // 使用自定义格式化程序处理所有响应。
            if (all && customized) return formatter.Invoke(value);

            // 使用默认格式化。
            if (value is ApiModel model) return model;
            if (value is string text) return new ApiTextModel(text);
            if (value is byte[] bytes) return new ApiBytesModel(bytes);
            if (value is Json json) return new ApiJsonModel(json);
            if (value is HttpActionResult har) return har.ToModel();
            if (value is HttpResponseMessage hrm) return hrm.ToModel();

            // 指定了自定义格式化程序。
            if (customized) return formatter.Invoke(value);

            // 默认。
            return isNull ? null : new ApiTextModel(value.ToString());
        }

        #endregion

    }

}
